#include "searchwindow.h"

/* Constructor
 * The constructor sets the parent window to the given QWidget
 * It then reads in the searchList.txt file name stores each line
 * as an instance of searchItem in a vector.
 * If the searchList is not able to be read, an appropriate error message is displayed
 */
SearchWindow::SearchWindow()
{
    QMainWindow t;

    //get current working directory path and set default video and image
    dir = QDir::currentPath();

    QString file_location = dir+"/searchList.txt";
    std::ifstream searchFile(file_location.toStdString().c_str());

    //read in from search data file
    char line[100];
    if(searchFile)
    {
        while(!searchFile.eof())//until the end of the file is reached
        {
            searchFile.getline(line, 100);//read in a line (max 100 characters, change later if needed)
            searchItems.push_back(searchItem(line));//make instance of searchItem and add to list of searchItems
        }
    }
    else
    {
        //Error checking========================================
        QMessageBox msgBox;
        msgBox.setText("Could not locate the database index file!\t\t");
        msgBox.setInformativeText("See details for more information.");
        msgBox.setStandardButtons(QMessageBox::Ok);
        msgBox.setDefaultButton(QMessageBox::Ok);
        msgBox.setIcon(QMessageBox::Critical);
        QString detail = "The text file named \"searchList.txt\" could not be found. This "
                         "file is necessary in order for the program to index the sign language "
                         "database and can not function without it. To fix this problem, make sure "
                         "that a copy of \"searchList.txt\" is in your SignLanguage folder or simply "
                         "reinstall the program.";
        msgBox.setDetailedText(detail);
        msgBox.exec();
        //run assertion which will close the program in an error code, specifying where the error occured.
        assert(searchFile);
        //End Error checking====================================
    }

    searchFile.close();//close file for safety sake

    //remove the last one if it was an extra line
    if(searchItems.back().getName()=="")
    {
        searchItems.pop_back();
    }
}

/* StartConnections Method
 * This method takes in the ui for the search window
 * and sets up the connection between all the signals and slots.
 */
void SearchWindow::startConnections(Ui_SearchWindow* UI)
{
    uis = UI;
    //connect signals and slots
    connect(uis->lineEdit, SIGNAL(returnPressed()), this, SLOT(typingFinished()));
    connect(uis->searchButton, SIGNAL(clicked()), this, SLOT(typingFinished()));
}

/* SearchAndShow Method
 * This method is used to show the search window. It takes in a QString
 * called text which it sets as the text in the lineEdit of the search window.
 * Depending on the QString given, it then calls the appropriate method display
 * the search results. FillListWidget method is called if a blank string has been passed,
 * while the updateListWidget method is called otherwise.
 */
void SearchWindow::searchAndShow(QString text)
{
    uis->lineEdit->setText(text);
    QString blank = "";
    QString defaultstr = "Type your word here:";

    if((blank.compare(uis->lineEdit->text())==0)||(defaultstr.compare(uis->lineEdit->text())==0))
    {
        fillListWidget();//if it is the default or blank text, fill the widget with all possible search items
    }
    else//same as in return presses slot
    {
        updateListWidget(uis->lineEdit->text());//change widget to display all possible search matches
    }
}

/* FillListWidget Method
 * This method is used to fill the list widget with all possible database items,
 * by first clearing anything that was in the widget before and then adding each database
 * item as a new QListWidgetItem, setting the parents widget to be the listWidget.
 * It sets the first item on the list to be selected.
 * It is called when a blank string was entered into the search window.
 */
void SearchWindow::fillListWidget()
{
    uis->listWidget->clear();
    for(unsigned int i=0; i<searchItems.size(); i++)
    {
        QListWidgetItem *item = new QListWidgetItem(searchItems[i].getName(), uis->listWidget);//add a new item to the listWidget
        if(i==0)
        {
            item->setSelected(true);
        }
    }
 }

/* UpdateListWidget Method
 * This method is used to update the list widget to contain only items that match the search word(s).
 * After clearing any previous items from the listWidget, it uses a vector of matches to the given QString
 * to repopulate the listWidget. To do this it creates a new QListWidgetItem for each matching word
 * and sets the parent widget to be the listWidget.
 */
void SearchWindow::updateListWidget(QString text)
{
    std::vector<searchItem> matches = find(text.toLower(), false);
    uis->listWidget->clear();//if there is no match to the word typed, the widget will be blank
    if(matches.size()>0)//show the word they selected and synonyms
    {
        for(unsigned int i=0; i<matches.size(); i++)
        {
            QListWidgetItem *item = new QListWidgetItem(matches[i].getName(), uis->listWidget);
            uis->listWidget->addItem(item); //add a new item to the listWidget
            if(i==0)
            {
                item->setSelected(true);
            }
        }
        removeDuplicates(); //remove any duplicates that may have occured
    }
}

/* Find Method
 * This method will look throught the searchItems for matches (either in name or in synonyms)
 * for each word in the given text QString. It returns a vector of all possible results,
 * starting with any direct name matches and then followed by the synonym matches
 */
std::vector<searchItem> SearchWindow::find(QString text, bool findJustName)
{
    //check if there are more than one words or not...
    QStringList words = text.split(" ");

    std::vector<searchItem> results;

    //first check through the name matches
    for(int w=0; w<words.length(); w++)
    {
       std::vector<searchItem> clone = searchItems;//in order to retain the origanal list
       QString name = words[w];
       bool foundNameMatch = false;//there will only be one name match per word

       while(!clone.empty())
       {
           if(name.compare(clone.back().getName())==0 && foundNameMatch == false)
           {
               results.push_back(clone.back());
               foundNameMatch = true;
           }
           clone.pop_back();//remove the item just checked
       }
    }
    //if only the name of the item is required, and not synonyms, skip the next part
    if(findJustName)
    {
        return results;
    }
    else//check through the synonyms as well
    {
        for(int w=0; w<words.length(); w++)
        {
            std::vector<searchItem> clone = searchItems;//in order to retain the origanal list
            QString name = words[w];
            while(!clone.empty())
            {
                for(unsigned int i=0; i<clone.back().getSynonyms().size(); i++)//check synonyms
                {
                    if(name.compare(clone.back().getSynonyms()[i])==0)
                    {
                        results.push_back(clone.back());
                    }
                }
                clone.pop_back();//remove the item just checked
            }
        }
        return results;
    }
}

/* RemoveDuplicates Method
 * Searches through the listWidget and hides any duplicates that occur
 */
void SearchWindow::removeDuplicates()
{
    for(int i=0; i<uis->listWidget->count(); i++)
    {
        for(int j=i+1; j<uis->listWidget->count(); j++)
        {
            if(uis->listWidget->item(i)->text().compare(uis->listWidget->item(j)->text())==0)
            {
               uis->listWidget->item(j)->setHidden(true);
            }
        }
    }
}

/* Typing Finished Slot
 * This slot handles what happens when the user has finished typing the word
 * It calls the methods to update the listWidget as needed
 */
void SearchWindow::typingFinished()
{
    QString blank = ""; //blank sting to be used to compare to the input --> if its blank the user must be able to see all the options
    if(blank.compare(uis->lineEdit->text())==0)//show all
    {
        fillListWidget();
    }
    else //show search matches --> will be blank if there are no matches
    {
        updateListWidget(uis->lineEdit->text());
    }
}

/* Destructor
 * There is nothing that needs to be deleted, so this method is empty
 */
SearchWindow::~SearchWindow()
{

}
